########################################################################################
##  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING THIS SOFTWARE
##
##  By downloading, copying, installing or using the software you agree to this license.
##  If you do not agree to this license, do not download, install, copy or use the software.
##
## Copyright (C) 2013, AerVision Technologies Pty Ltd, all rights reserved.
##
## Redistribution and use in source and binary forms, with or without modification,
## are permitted provided that the following conditions are met:
##
##   * Redistribution's of source code must retain the above copyright notice,
##     this list of conditions and the following disclaimer.
##
##   * Redistribution's in binary form must reproduce the above copyright notice,
##     this list of conditions and the following disclaimer in the documentation
##     and/or other materials provided with the distribution.
##
##   * The name of AerVision Technologies, may not be used to endorse or promote products
##     derived from this software without specific prior written permission.
##
## This software is provided by the copyright holders and contributors to demonstrate 
## www.AerCloud.net Framework as a Service API (FaaS), "as is" and
## any express or implied warranties, including, but not limited to, the implied
## warranties of merchantability and fitness for a particular purpose are disclaimed.
## In no event shall the copyright holder or contributors be liable for any direct,
## indirect, incidental, special, exemplary, or consequential damages
## (including, but not limited to, procurement of substitute goods or services;
## loss of use, data, or profits; or business interruption) however caused
## and on any theory of liability, whether in contract, strict liability,
## or tort (including negligence or otherwise) arising in any way out of
## the use of this software, even if advised of the possibility of such damage.
########################################################################################

import glob
import os
import sys
import webbrowser
import logging

from Tkinter import Tk, Toplevel, Frame, Label, Entry, Button, StringVar
from ScrolledText import ScrolledText
from ttk import Notebook

from aercloudclient.utils.AerCloudNet_Interface import AerCloudNet_Interface
from aercloudclient.utils.Settings import Settings
from aercloudclient.utils.ImageData import ImageData

from aercloudclient.gui.widgets import AutoWrapLabel, ScrolledListbox, WidgetLogger, AutoSizePhotoFrame
from aercloudclient.gui.BrowseDatabaseWindow import BrowseDatabaseWindow
from aercloudclient.gui.BrowseSearchResultsWindow import BrowseSearchResultsWindow
                    
class FrameStep1(Frame):
    def __init__(self, master=None, controller=None):
        Frame.__init__(self, master, name='frame-step1')
        self.controller = controller
        frame1 = Frame(self)
        Label(frame1, text="Enter Access Key:").pack(side='left', anchor='n')
        self.ventry = StringVar()
        self.ventry.trace('w', self.onEntryChange)
        self.ventry.set(Settings.Default.AccessKey)
        self.entry = Entry(frame1, width=20, textvariable=self.ventry)
        self.entry.pack(side='left',anchor='n', padx=10, pady=3)
        # Button to quit app on right
        btn = Button(frame1, text="Connect", command=self.onConnect)
        btn.pack(side='left',anchor='n', padx=10)

        Label(frame1, text="You can obtain an access key from ").pack(side='left',anchor='n')
        link = Label(frame1, text='AerCloud.net', foreground="#0000ff")
        link.bind("<1>", lambda event: webbrowser.open('http://www.aercloud.net'))
        link.pack(side='left',anchor='n')
        frame1.pack(side='top', fill='both', padx=10, pady=10)
        note = AutoWrapLabel(self, anchor='w', justify='left', wraplength = 600, text='Please note some of the service calls in this demo software require call credit. It is recommended to use a free basic account for testing this software.', foreground="#ff0000")
        note.pack(side='top', fill='both', padx=10, pady=10)

    def onEntryChange(self, *args):
        Settings.Default.AccessKey = self.ventry.get()
        
    def onConnect(self):
        self.after(0, self.controller.Connect)
        
class FrameStep2(Frame):
    def __init__(self, master=None, controller=None):
        Frame.__init__(self, master, name='frame-step2')
        self.controller = controller
        self.grid_columnconfigure(0, minsize=180, weight=0)
        self.grid_columnconfigure(1, minsize=40, weight=1)
        self.grid_columnconfigure(2, minsize=128, weight=0)
        self.grid_rowconfigure(0, minsize=23, weight=0)
        self.grid_rowconfigure(1, minsize=3, weight=1)
        btn = Button(self, text="Click to save", command=self.onSave)
        btn.grid(row=0, column=0, sticky='nwe', padx=3, pady=3)
        self.listbox = ScrolledListbox(self, selectmode='browse', width=32)
        self.listbox.grid(row=1, column=0, sticky='nswe', padx=3, pady=3)
        self.lblFaceImage = Label(self)
        self.lblFaceImage.grid(row=1,column=2,rowspan=1, sticky='s')
        self.filelist = []            
        self.listbox.bind('<<ListboxSelect>>', lambda event, arg=None: self.onSelect(event, arg))
        types = ('*.jpg', '*.png')
        for files in types:
            self.filelist.extend(glob.glob(r"./Resources/DB/" + files))
        self.filelist = sorted(self.filelist)
        for f in self.filelist:
            self.listbox.insert('end', os.path.basename(f))
        self.currentImage = None
        self.faceImage = None
        
    def onSelect(self, evt, arg):
        if len(self.filelist) == 0:
            return
        w = evt.widget
        index = int(w.curselection()[0])
        value = self.filelist[index]
        if self.currentImage:
            self.currentImage.grid_forget()
            self.currentImage = None
        if self.faceImage:
            self.faceImage = None            
        self.imagename = os.path.splitext(os.path.basename(value))[0]
        self.currentImage = AutoSizePhotoFrame(self, file=value, label=self.imagename)
        self.currentImage.grid(row=0, column=1, rowspan=2, sticky='news')
                
    def onSave(self):
        sel = self.listbox.curselection()
        if len(sel) == 0:
            return
        index = int(self.listbox.curselection()[0])
        value = self.filelist[index]        
        personName, imageName = self.controller.SaveImage(value)
        if personName == None:
            return
        self.filelist.pop(index)
        self.listbox.delete(index)
        imageData = AerCloudNet_Interface.GetImage(personName, imageName)
        self.faceImage = imageData.asThumbnail(128, 128)
        self.lblFaceImage.configure(image=self.faceImage)
        
class FrameStep3(Frame):
    def __init__(self, master=None, controller=None):
        Frame.__init__(self, master, name='frame-step3')
        self.controller = controller
        btn = Button(self, text="Browse Database...", command=controller.BrowseDatabase)
        btn.pack(side='top', fill='both', anchor='n', padx=10, pady=10)
        self.pack()        
        
class FrameStep4(Frame):
    def __init__(self, master=None, controller=None):
        Frame.__init__(self, master, name='frame-step4')
        self.controller = controller
        self.grid_columnconfigure(0, minsize=180, weight=0)
        self.grid_columnconfigure(1, minsize=40, weight=1)
        self.grid_columnconfigure(2, minsize=128, weight=0)
        self.grid_rowconfigure(0, minsize=23, weight=0)
        self.grid_rowconfigure(1, minsize=23, weight=1)
        self.grid_rowconfigure(2, minsize=23, weight=0)

        self.listbox = ScrolledListbox(self, selectmode='browse', width=32)
        self.listbox.grid(row=0, rowspan=2, sticky='news', padx=3, pady=3)
        self.filelist = []            
        self.listbox.bind('<<ListboxSelect>>', lambda event, arg=None: self.onSelect(event, arg))
        types = ('*.jpg', '*.png')
        for files in types:
            self.filelist.extend(glob.glob(r"./Resources/Query/" + files))
        self.filelist = sorted(self.filelist)
        for f in self.filelist:
            self.listbox.insert('end', os.path.basename(f))
        btn = Button(self, text="Click to initiate face search...", command=self.onSearch, width=32)
        btn.grid(row=2, sticky='news', padx=3, pady=3)
        self.imgQueryImage = None
        
    def onSelect(self, evt, arg):
        w = evt.widget
        sel = w.curselection()
        if len(sel) == 0:
            return
        index = int(sel[0])
        value = self.filelist[index]
        if self.imgQueryImage:
            self.imgQueryImage.grid_forget()
            self.imgQueryImage = None
        self.imgQueryImage = AutoSizePhotoFrame(self, file=value)
        self.imgQueryImage.grid(row=0, column=1, rowspan=3, columnspan=2, sticky='news')
        
    def SearchImage(self, imageName, image):
        if image == None:
            return
            
        imageData = AerCloudNet_Interface.DetectFaces(imageName, image)
        i = 1
        for data in imageData:
            self.controller.BrowseSearchResults(imageName + "[" + str(i) + "]", data)
            i += 1
        
    def onSearch(self):
        sel = self.listbox.curselection()
        if len(sel) == 0:
            return
        index = int(sel[0])
        value = self.filelist[index]
        imageData = ImageData(value)
        self.SearchImage(value, imageData)
                
class MainWindow:
    """MainWindow
    """
    def __init__(self, root):
        self.root = root
        # create Frame in "root"
        self.master = Frame(root, name='mainFrame')
        # fill both sides of the parent
        self.master.pack(fill='both', expand=1)

        root.title("www.AerCloud.net Demo")
        # quit if the window is deleted
        root.protocol("WM_DELETE_WINDOW", self.onClose)

        # set up logging to file and console
        logging.basicConfig(level=logging.DEBUG,
                            format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
                            datefmt='%m-%d %H:%M',
                            filename='aercloud.log',
                            filemode='w')
        # define a Handler which writes INFO messages or higher to the sys.stderr
        console = logging.StreamHandler()
        console.setLevel(logging.INFO)
        # set a format which is simpler for console use
        formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
        # tell the handler to use this format
        console.setFormatter(formatter)
        # add the handler to the root logger
        logging.getLogger().addHandler(console)

        self.master.grid_columnconfigure(0, minsize=640, weight=1)
        self.master.grid_rowconfigure(0, minsize=250, weight=1)
        self.master.grid_rowconfigure(1, minsize=3, weight=0)
        self.master.grid_rowconfigure(2, minsize=142, weight=1)
        
        # create Notebook in "master"
        nb = Notebook(self.master, name='nb', width=620)
        # fill "master" but pad sides
        nb.grid(row=0, column=0, padx=10, pady=2, sticky='nswe')

        # create each Notebook tab in a Frame and add tab to Notebook
        frame_step1 = FrameStep1(self.master, self)
        nb.add(frame_step1, text="Step 1.Access Key")
                
        # repeat for each tab
        self.frame_step2 = FrameStep2(self.master, self)
        nb.add(self.frame_step2, text="Step 2.Save Face")

        frame_step3 = FrameStep3(self.master, self)
        nb.add(frame_step3, text="Step 3.Browse Database")

        frame_step4 = FrameStep4(self.master, self)
        nb.add(frame_step4, text="Step 4.Search Face")

        self.txtStatus = ScrolledText(self.master, height=10, state='disabled', width=50)
        self.txtStatus.grid(row=2, column=0, padx=10, pady=2, sticky='nswe')

        # set up logging to widget
        self.logger = WidgetLogger(self.txtStatus)
        self.logger.setLevel(logging.INFO)
        formatter = logging.Formatter('%(asctime)s | %(message)s', datefmt='%I:%M:%S %p')
        self.logger.setFormatter(formatter)
        logging.getLogger().addHandler(self.logger)
                
        self.search_browser = {}
        
        AerCloudNet_Interface.InitConnection(self)
        

    def BrowseDatabase(self):
        self.newWindow = Toplevel(self.master)
        self.database_browser = BrowseDatabaseWindow(self.newWindow)

    def BrowseSearchResults(self, imageName, image):
        self.newWindow = Toplevel(self.master)
        win = BrowseSearchResultsWindow(self.newWindow, self, imageName, image)
        self.search_browser[win] = win
    
    def Connect(self):
        AerCloudNet_Interface.InitConnection(self)
        
    def SaveImage(self, fileName):
        image = ImageData(fileName)
        imageName = os.path.splitext(os.path.basename(fileName))[0]
        personName = imageName.split('.')[0]
        imageNames = AerCloudNet_Interface.GetImageNames(personName)
        if imageNames != None:
            for i in imageNames:
                logging.debug(i)
        if imageNames != None and imageName in imageNames:
            logging.info("[" + imageName + "] " + "Already saved")
            result = True
        else:
            result = AerCloudNet_Interface.SaveImage(personName, imageName, image)
        if result:
            return personName, imageName
        else:
            return None, None
    
    def onClose(self):
        Settings.Save()
        self.master.quit()
        